import { updateNodeElement } from "../DOM"
import { createTaskQueue, arrified, createStateNode, getTag, getRoot } from "../Misc"

const taskQueue = createTaskQueue()
let subTask = null
let pendingCommit = null

const commitAllWork = fiber => {
    fiber.effects.forEach(item => {
        if (item.tag === "class_component") {
            item.stateNode.__fiber = item
        }

        if (item.effectTag === "delete") {
            item.parent.stateNode.removeChild(item.stateNode)
        } else if (item.effectTag === "update") {
            // 节点更新
            if (item.type === item.alternate.type) {
                // 节点类型相同
                updateNodeElement(item.stateNode, item, item.alternate)
            } else {
                // 节点类型不同
                item.parent.stateNode.replaceChild(
                    item.stateNode,
                    item.alternate.stateNode
                )
            }
        } else if (item.effectTag === "placement") {
            let fiber = item
            let parentFiber = item.parent
            while (parentFiber.tag === "class_component" || parentFiber.tag === "function_component") {
                // 循环向上查找，直到找到不是类组件的父节点
                parentFiber = parentFiber.parent
            }
            if (fiber.tag === "host_component") {
                parentFiber.stateNode.appendChild(fiber.stateNode)
            }
        }
    })

    // 备份旧的 fiber 对象
    fiber.stateNode.__rootFiberContainer = fiber
}

const getFirstTask = () => {
    // 从任务队列中获取任务
    const task = taskQueue.pop()
    if (task.from === "class_component") {
        const root = getRoot(task.instance)
        task.instance.__fiber.partialState = task.partialState
        return {
            props: root.props,
            stateNode: root.stateNode,
            tag: "host_root",
            effects: [],
            child: null,
            alternate: root
        }
    }
    // 返回最外层节点的 fiber 对象
    return {
        props: task.props,
        stateNode: task.dom,
        tag: "host_root",
        effects: [],
        child: null,
        alternate: task.dom.__rootFiberContainer
    }
}

const reconcileChildren = (fiber, children) => {
    
    /**
     * children 可能是对象也可能是数组
     * 将 children 转换成数组
     */
    const arrifiedChildren = arrified(children)
    
    let index = 0
    let numberOfElements = arrifiedChildren.length
    let element = null
    let newFiber = null
    let prevFiber = null

    let alternate = null
    if (fiber.alternate && fiber.alternate.child) {
        alternate = fiber.alternate.child
    }

    while (index < numberOfElements || alternate) {
        element = arrifiedChildren[index]
        
        if (element && alternate) {
            // 节点更新
            newFiber = {
                type: element.type,
                props: element.props,
                tag: getTag(element),
                effects: [],
                effectTag: "update",
                stateNode: null,
                parent: fiber,
                alternate
            }
            if (element.type === alternate.type) {
                // 类型相同
                newFiber.stateNode = alternate.stateNode
            } else {
                // 类型不同
                newFiber.stateNode = createStateNode(newFiber)
            }
        } else if (element && !alternate) {
            // 初始渲染
            newFiber = {
                type: element.type,
                props: element.props,
                tag: getTag(element),
                effects: [],
                effectTag: "placement",
                stateNode: null,
                parent: fiber
            }
            // 如果当前节点是普通节点 stateNode 要存储节点本身，如果是组件节点，需要存储组件实例对象
            newFiber.stateNode = createStateNode(newFiber)
        } else if (!element && alternate) {
            // 删除操作
            alternate.effectTag = "delete"
            fiber.effects.push(alternate)
        }

        
        if (index == 0) {
            // 如果是第一个子节点  则为父节点的 fiber 对象添加 child 属性
            fiber.child = newFiber
        } else if (element) {
            // 如果不是第一个子节点  则为前一个兄弟节点的 fiber 对象添加 sibing 属性
            prevFiber.sibling = newFiber
        }

        if (alternate && alternate.sibling) {
            alternate = alternate.sibling
        } else {
            alternate = null
        }

        prevFiber = newFiber

        index++
    }
}

const executeTask = fiber => {
    if (fiber.tag === "class_component") {
        if (fiber.stateNode.__fiber && fiber.stateNode.__fiber.partialState) {
            fiber.stateNode.state = {
                ...fiber.stateNode.state,
                ...fiber.stateNode.__fiber.partialState
            }
        }
        // 如果是类组件，调用实例的 render 方法获取子节点
        reconcileChildren(fiber, fiber.stateNode.render())
    } else if (fiber.tag === "function_component") {
        // 如果是函数组件，调用函数本身来获取子节点
        reconcileChildren(fiber, fiber.stateNode(fiber.props))
    } else {
        reconcileChildren(fiber, fiber.props.children)
    }
    
    if (fiber.child) {
        return fiber.child
    }

    let currentExecutelyFiber = fiber

    while (currentExecutelyFiber.parent) {
        // 为父级节点的 effects 添加当前节点的 fiber.effects 和 当前节点的 fiber
        currentExecutelyFiber.parent.effects = currentExecutelyFiber.parent.effects.concat(
            currentExecutelyFiber.effects.concat([currentExecutelyFiber])
        )
        if (currentExecutelyFiber.sibling) {
            return currentExecutelyFiber.sibling
        }
        currentExecutelyFiber = currentExecutelyFiber.parent
    }
    pendingCommit = currentExecutelyFiber
}

const workLoop = deadline => {
    // 如果没有子任务则去获取子任务
    if (!subTask) {
        subTask = getFirstTask()
    }
    // 有任务存在且浏览器空闲就会执行
    while (subTask && deadline.timeRemaining() > 1) {
        // executeTask 方法会执行任务并返回一个新的任务，这样才会循环
        subTask = executeTask(subTask)
    }

    if (pendingCommit) {
        commitAllWork(pendingCommit)
    }
}

const performTask = deadline => {
    // 执行任务
    workLoop(deadline)
    // 判断任务队列中是否还有任务没有执行
    if (subTask || !taskQueue.isEmpty()) {
        // subTask 有任务或 isEmpty 方法返回的队列长度不为 0
        // 让浏览器空闲时继续执行
        requestIdleCallback(performTask)
    }
}

export const render = (element, dom) => {
    /**
     * 1. 向任务队列中添加任务
     * 2. 指定在浏览器空闲时执行任务
     */

    /**
     * 任务就是通过 vdom 对象 构建的 fiber 对象
     * 
     */
    taskQueue.push({
        dom,
        props: { children: element }
    })
    // 在浏览器空闲时间执行任务
    requestIdleCallback(performTask)
}

export const scheduleUpdate = (instance, partialState) => {
    taskQueue.push({
        from: "class_component",
        instance,
        partialState
    })
    requestIdleCallback(performTask)
}